package com.three.zk.lock;

import com.three.config.common.IConfig;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;

/**
 * 使用<br>
 *     DistributedLock lock = null;
 *     try {
 *         lock = new DistributedLock("你要锁住的id");
 *         lock.lock();
 *         // 你的业务逻辑
 *     } finally {
 *         if(lock != null)
 *              lock.unlock();
 *     }
 *
 * zk分布式锁
 * Created by Mathua on 2017/6/28.
 */
public class DistributedLock {

    private static ZooKeeper zooKeeper;
    private final static String root = "/lock";
    private static Object lock = new Object();

    public static ZooKeeper getZK() {
        if(zooKeeper == null) {
            synchronized (lock) {
                if(zooKeeper == null) {
                    try {
                        zooKeeper = new ZooKeeper(IConfig.chess.zk.server_address, 10 * 1000, new Watcher() {
                            @Override
                            public void process(WatchedEvent event) {

                            }
                        });
                    } catch (IOException e) {
                        zooKeeper = null;
                    }
                }
            }
        }
        return zooKeeper;
    }

    private WriteLock writeLock;

    /**
     * 创建分布式锁
     * @param lockName 竞争资源标志，locnName中不能包含单词_lock_
     */
    public DistributedLock(String lockName) {
        writeLock = new WriteLock(getZK(), root + "/" + lockName, null);
    }

    /**
     * 创建分布式锁
     * @param lockName 竞争资源标志，locnName中不能包含单词_lock_
     * @param callback 回调
     */
    public DistributedLock(String lockName, LockListener callback) {
        writeLock = new WriteLock(getZK(), root  + "/" + lockName, null, callback);
    }

    /**
     * 获取锁
     * @return 是否成功获取到锁
     */
    public boolean lock() {
        String splitStr = "_lock_";
        if(writeLock.getDir().contains(splitStr))
            throw new LockException("lockName can not contains \\u000B");
        try {
            while (true) {
                if(writeLock.lock())
                    return true;
//                Thread.sleep(1);
            }
        } catch (KeeperException e) {
            e.printStackTrace();
            return false;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 解锁
     */
    public void unlock() {
        writeLock.unlock();
    }

    public class LockException extends RuntimeException {
        private static final long serialVersionUID = 1L;
        public LockException(String e){
            super(e);
        }
        public LockException(Exception e){
            super(e);
        }
    }
}